package spring_learn.a20;

import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.nio.charset.StandardCharsets;
import java.util.Map;

/*
1. DispatcherServlet 是在第一次被访问时执行初始化, 也可以通过配置修改为 Tomcat 启动后就初始化
2. 在初始化时会从 Spring 容器中找一些 Web 需要的组件, 如 HandlerMapping、HandlerAdapter 等，并逐一调用它们的初始化
3. RequestMappingHandlerMapping 初始化时，会收集所有 @RequestMapping 映射信息，封装为 Map，其中
   * key 是 RequestMappingInfo 类型，包括请求路径、请求方法等信息
   * value 是 HandlerMethod 类型，包括控制器方法对象、控制器对象
   * 有了这个 Map，就可以在请求到达时，快速完成映射，找到 HandlerMethod 并与匹配的拦截器一起返回给 DispatcherServlet
4. RequestMappingHandlerAdapter 初始化时，会准备 HandlerMethod 调用时需要的各个组件，如：
   * HandlerMethodArgumentResolver 解析控制器方法参数
   * HandlerMethodReturnValueHandler 处理控制器方法返回值
 */
public class A20 {


    public static void main(String[] args) throws Exception {
        AnnotationConfigServletWebServerApplicationContext annotationConfigServletWebServerApplicationContext
                =new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
        RequestMappingHandlerMapping handlerMapping = annotationConfigServletWebServerApplicationContext.getBean(RequestMappingHandlerMapping.class);
        Map<RequestMappingInfo, HandlerMethod> handlerMethods = handlerMapping.getHandlerMethods();
        /*
        {GET [/test1]}----spring_learn.a20.Controller1#test1()
        {POST [/test2]}----spring_learn.a20.Controller1#test2(String)
        { [/test4]}----spring_learn.a20.Controller1#test4()
        {PUT [/test3]}----spring_learn.a20.Controller1#test3(String)

        key就是请求路径的信息，value就是对应的controller中的方法的信息
         */
        handlerMethods.forEach((k,v)->{
            System.out.println(k+"----"+v);
        });

        //请求来了，获取控制器中对应的方法，返回的是一个 执行链对象
        //模拟一个http请求
        MockHttpServletRequest mockHttpServletRequest = new MockHttpServletRequest();
        mockHttpServletRequest.setMethod("PUT");
        mockHttpServletRequest.setRequestURI("/test4");
        //会通过自定义的token resolver处理，并且将对应的参数赋值成request中toke对应的这个value
        mockHttpServletRequest.addHeader("token","我是一个测试token！");
        //http响应
        MockHttpServletResponse mockHttpServletResponse = new MockHttpServletResponse();
        HandlerExecutionChain chain = handlerMapping.getHandler(mockHttpServletRequest);
        //HandlerExecutionChain with [spring_learn.a20.Controller1#test4()] and 0 interceptors     控制器中的方法+过滤器
        System.out.println(chain);
        System.out.println("-------------------");

        //这个adapter对象的invokeHandlerMethod方法负责从request中获取请求路径，并且找到对应方法进行实际执行
        MyRequestMappingHandlerAdapter adapter = annotationConfigServletWebServerApplicationContext.getBean(MyRequestMappingHandlerAdapter.class);
        adapter.invokeHandlerMethod(mockHttpServletRequest, mockHttpServletResponse, (HandlerMethod) chain.getHandler());

        //所有的参数解析器
        /*System.out.println("所有的参数解析器----");
        for (HandlerMethodArgumentResolver argumentResolver : adapter.getArgumentResolvers()) {
            System.out.println(argumentResolver);
        }
        //所有的返回值解析器
        System.out.println("所有的返回值解析器-----");
        for (HandlerMethodReturnValueHandler hander : adapter.getReturnValueHandlers()) {
            System.out.println(hander);
        }*/

        //因为/test4 对应的方法有yml注解，返回值会经过自定义的yml return resolver解析，变成yml格式 并且返回
        byte[] contentAsByteArray = mockHttpServletResponse.getContentAsByteArray();
        System.out.println(new String(contentAsByteArray, StandardCharsets.UTF_8));

    }
}
